查看原文
其他

.NET 定义 Static 字段还有顺序要求?

My IO DotNet 2022-07-19

前言


发现一个bug,代码结构类似下面的示例。你能说出这段代码的正确返回结果吗?

class Program  
{  
    private static int a1 = a2;  
    private static int a2 = Init();  
  
    private static int Init()  
    {  
        return 123;  
    }  
  
    static void Main(string[] args)  
    {  
        Console.WriteLine($@"{a1} {a2}");  
    }  
}  

答案是0 123!

如果改成这样则返回123 123

private static int a2 = Init();  
private static int a1 = a2;  

定义static字段还有顺序要求?微软官方文档[1]明明是这样说的:

在首次访问静态成员之前以及在调用构造函数(如果有)之前,会初始化静态成员。

不是应该a2被访问之前会初始化吗?

原因

通过C#代码看不出问题,我们看看IL代码[2]实现:


  • ldsfld 将静态字段的值推送到计算堆栈上
  • stsfld 用来自计算堆栈的值替换静态字段的值

原来,静态字段的初始化,是在所在类的静态构造函数中,按照定义的顺序依次完成的。由于a1和a2是在同一个类里定义的,为a1赋值时a2还没有值,所以使用的是int类型默认值。

解决方法

为了避免这个问题,最好不使用同一个类里的静态字段用于初始化,类似这样:

private static int a1 = OtherClass.a2; 

如果就是要这样用,可以改成这样的写法:

private static int b1 = b2;  
private static int b2 => Init();  

通过IL代码可以看到:

b2被转换成get属性,因此不用初始化。

结论

细节是魔鬼!原来定义static字段还真有顺序要求


- EOF -

推荐阅读  点击标题可跳转
C# 条形码图像生成库.NET 7 预览版来啦,我升级体验了.NET 中更方便地模拟 Http 响应


看完本文有收获?请转发分享给更多人

推荐关注「DotNet」,提升.Net技能 

点赞和在看就是最大的支持❤️

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存